/*
 * Simple test vm implementations for the language, using gcc extensions.
 * The test program is next comment is "statically" executed by the vms:
 * VM == 0:
 *	simple "previous" byte size opcodes implementation,
 *	without "peephole" opcodes, requires a noop before load of immediate
 * VM == 1:
 *	match "previous" implementation, with byte size opcodes, and
 *	addition of the current simple "peeophole" opcodes
 * VM == 2:
 *	better match the current vm, using int32 sized opcodes, but
 *	without "peeophole" opcodes, also uses a "void **routine"
 *	label (that when added, increased performance by 20%)
 * VM == 3:
 *	matches the current vm (15/11/2009)
 * VM == 4:
 *	experiment with better flow control and code reuse, to make it
 *	easier to write complex opcodes, by using several functions,
 *	also, using byte sized opcodes
 * VM == 5:
 *	experiment with code patched to have the jump address directly
 *	in the "program" instead of dereferencing a label table
 *	this is not a big issue for the vm/language concept, but may
 *	significantly increase load time
 *	not an issue because the infrastructure (compiler/runtime), including
 *	source code is expected to be always available, the language is
 *	expected to be used in trusted environments and for research
 *	and experiments, where knowledge of internals is not a "secret"
 * VM == 6:
 *	same as VM == 5, but with extra "peephole" opcodes
 * VM == 7:
 *	shows an experiment with a previous idea, not "forbidden", where
 *	the language would directly generate C source code, and call the
 *	compiler to generate a binary
 *
 *

------------------------------------------------------------------------
timings on a "low end computer":

$ gcc -O0 check/vm-test.c -DVM=0; time /bin/sh -c './a.out'
49999995000000
2.43user 0.00system 0:02.65elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=1; time /bin/sh -c './a.out' 
49999995000000
2.62user 0.00system 0:02.85elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=2; time /bin/sh -c './a.out' 
49999995000000
2.20user 0.00system 0:02.39elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=3; time /bin/sh -c './a.out' 
49999995000000
1.87user 0.00system 0:02.04elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=4; time /bin/sh -c './a.out' 
49999995000000
3.33user 0.00system 0:03.68elapsed 90%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=5; time /bin/sh -c './a.out' 
49999995000000
1.93user 0.00system 0:02.08elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=6; time /bin/sh -c './a.out' 
49999995000000
1.51user 0.00system 0:01.64elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=7; time /bin/sh -c './a.out' 
49999995000000
1.24user 0.00system 0:01.34elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O0 check/vm-test.c -DVM=9; time /bin/sh -c './a.out' 
49999995000000
1.03user 0.00system 0:01.05elapsed 98%CPU (0avgtext+0avgdata 4512maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

------------------------------------------------------------------------
and now with -O3:

$ gcc -O3 check/vm-test.c -DVM=0; time /bin/sh -c './a.out' 
49999995000000
1.75user 0.00system 0:01.87elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=1; time /bin/sh -c './a.out' 
49999995000000
1.54user 0.00system 0:01.69elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=2; time /bin/sh -c './a.out' 
49999995000000
1.35user 0.00system 0:01.48elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=3; time /bin/sh -c './a.out' 
49999995000000
1.01user 0.00system 0:01.09elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+443minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=4; time /bin/sh -c './a.out' 
49999995000000
1.73user 0.00system 0:01.88elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=5; time /bin/sh -c './a.out' 
49999995000000
1.26user 0.00system 0:01.40elapsed 90%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+441minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=6; time /bin/sh -c './a.out' 
49999995000000
0.99user 0.00system 0:01.08elapsed 91%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=7; time /bin/sh -c './a.out' 
49999995000000
0.09user 0.00system 0:00.10elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+442minor)pagefaults 0swaps

$ gcc -O3 check/vm-test.c -DVM=9; time /bin/sh -c './a.out' 
49999995000000
0.26user 0.00system 0:00.27elapsed 97%CPU (0avgtext+0avgdata 4496maxresident)k
0inputs+0outputs (0major+440minor)pagefaults 0swaps

------------------------------------------------------------------------
VM==7 with -O3 is very close to the best possible result (actually, gcc just
prints the result when compiling the equivalent C program with anything
larger then -O0), but, when adding type simulation (not in this file),
it drastically drops performance, because it cannot be optimized properly,
and values are no longer kept in registers, etc, becoming one of the worst
cases...

The goal for now is to keep roughly on the logic of VM==3

UPDATE. Doesn't matter if gcc -O3 or -O0, using gnu lightning,
but using 32 bit values, that overflow for this test:
$ gcc -O0 check/vm-test.c -DVM=8; time /bin/sh -c './a.out'
-2014260032
0.20user 0.00system 0:00.20elapsed 97%CPU (0avgtext+0avgdata 4512maxresident)k
0inputs+0outputs (0major+461minor)pagefaults 0swaps

 */

#include <stdio.h>
#include <stdlib.h>

#ifndef VM
#define VM	3
#endif

/*
auto a, b;
for (a = b = 0; a < 10000000; ++a)
	b += a;
print("%d\n", a);
 */

#if VM == 0
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
#define NOOP	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define J	7
#define JT	8
#define IMM	9
#define END	10
    char program[] = {
	J,	2,	NOOP,	IMM,
	0,	0,	0,	0,
	0,	0,	0,	0,
	STORE,	0,	STORE,	1,
	/* L0: */
	LOAD,	1,	PUSH,	LOAD,
	0,	ADD,	STORE,	1,
	LOAD,	0,	INC,	STORE,
	0,	PUSH,	NOOP,	IMM,
	0x80,	0x96,	0x98,	0,
	0,	0,	0,	0,
	LT,	JT,	-26,	END
    };
    const void *codes[] = {
	&&noop,	&&load,	&&store, &&push,
	&&add,	&&inc,	&&lt,	&&j,
	&&jt,	&&imm,	&&end
    };
    char *ip;

    ip = program;
    p = s;

noop:
    goto *codes[*(unsigned char *)ip++];

load:
    r = v[*ip++];
    goto *codes[*(unsigned char *)ip++];

store:
    v[*ip++] = r;
    goto *codes[*(unsigned char *)ip++];

push:
    *p++ = r;
    goto *codes[*(unsigned char *)ip++];

add:
    r = *--p + r;
    goto *codes[*(unsigned char *)ip++];

inc:
    r = r + 1;
    goto *codes[*(unsigned char *)ip++];

lt:
    r = *--p < r;
    goto *codes[*(unsigned char *)ip++];

j:
    ip += *(char *)ip;
    goto *codes[*(unsigned char *)ip++];

jt:
    if (r)
	ip += *(char *)ip;
    else
	++ip;
    goto *codes[*(unsigned char *)ip++];

imm:
    r = *(long long *)ip;
    ip += sizeof(long long);
    goto *codes[*(unsigned char *)ip++];

end:
    printf("%lld\n", v[1]);
}

#elif VM == 1
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
#define NOOP	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define J	7
#define JT	8
#define IMM	9
#define END	10
    char program[] = {
	J,	2,	NOOP,	IMM,
	0,	0,	0,	0,
	0,	0,	0,	0,
	STORE,	0,	STORE,	1,
	/* L0: */
	LOAD,	1,	PUSH,	LOAD,
	0,	ADD,	STORE,	1,
	LOAD,	0,	INC,	STORE,
	0,	PUSH,	NOOP,	IMM,
	0x80,	0x96,	0x98,	0,
	0,	0,	0,	0,
	LT,	JT,	-26,	END
    };
    void *codes[] = {
	&&noop,	&&load,	&&store, &&push,
	&&add,	&&inc,	&&lt,	&&j,
	&&jt,	&&imm,	&&end
    };
    char  *ip;
    char   offset;
    void **routine;

    ip = program;
    p = s;

noop:
    routine = codes[*(unsigned char *)ip++];
    goto *routine;

load:
    offset = *ip++;
    routine = codes[*(unsigned char *)ip++];
    r = v[offset];
    goto *routine;

store:
    offset = *ip++;
    routine = codes[*(unsigned char *)ip++];
    v[offset] = r;
    goto *routine;

push:
    routine = codes[*(unsigned char *)ip++];
    *p++ = r;
    goto *routine;

add:
    routine = codes[*(unsigned char *)ip++];
    r = *--p + r;
    goto *routine;

inc:
    routine = codes[*(unsigned char *)ip++];
    r = r + 1;
    goto *routine;

lt:
    routine = codes[*(unsigned char *)ip++];
    r = *--p < r;
    goto *routine;

j:
    ip += *(char *)ip;
    routine = codes[*(unsigned char *)ip++];
    goto *routine;

jt:
    if (r)
	ip += *(char *)ip;
    else
	++ip;
    routine = codes[*(unsigned char *)ip++];
    goto *routine;

imm:
    r = *(long long *)ip;
    ip += sizeof(long long);
    routine = codes[*(unsigned char *)ip++];
    goto *routine;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 2
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
#define NOOP	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define J	7
#define JT	8
#define IMM	9
#define END	10
    int program[] = {
	IMM,	0,	0,	STORE,
	0,	STORE,	1,/*L:*/LOAD,
	1,	PUSH,	LOAD,	0,
	ADD,	STORE,	1,	LOAD,
	0,	INC,	STORE,	0,
	PUSH,	IMM,	0x989680,0,
	LT,	JT,	-19,	END
    };
    void *codes[] = {
	&&noop,	&&load,	&&store, &&push,
	&&add,	&&inc,	&&lt,	&&j,
	&&jt,	&&imm,	&&end
    };
    int  *ip;
    int   offset;
    void **routine;

    ip = program;
    p = s;

noop:
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

load:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    r = v[offset];
    goto *routine;

store:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    v[offset] = r;
    goto *routine;

push:
    routine = codes[*(unsigned int *)ip++];
    *p++ = r;
    goto *routine;

add:
    routine = codes[*(unsigned int *)ip++];
    r = *--p + r;
    goto *routine;

inc:
    routine = codes[*(unsigned int *)ip++];
    r = r + 1;
    goto *routine;

lt:
    routine = codes[*(unsigned int *)ip++];
    r = *--p < r;
    goto *routine;

j:
    ip += *(char *)ip;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

jt:
    if (r)
	ip += *(int *)ip;
    else
	++ip;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

imm:
    r = *(long long *)ip;
    ip += 2;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 3
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
#define NOOP		0
#define LOAD		1
#define STORE		2
#define PUSH		3
#define ADD		4
#define INC		5
#define LT		6
#define J		7
#define JT		8
#define IMM		9
#define LOAD_PUSH	10
#define STORE_PUSH	11
#define IMM_LT		12
#define END		13
    int program[] = {
	IMM,	0,		0,	STORE,
	0,	STORE,		1,/*L:*/LOAD_PUSH,
	1,	LOAD,		0,	ADD,
	STORE,	1,		LOAD,	0,
	INC,	STORE_PUSH,	0,	IMM_LT,
	0x989680,0,		JT,	-16,
	END
    };
    void *codes[] = {
	&&noop,		&&load,	&&store,	 &&push,
	&&add,		&&inc,	&&lt,		&&j,
	&&jt,		&&imm,	&&load_push,	&&store_push,
	&&imm_lt,	&&end
    };
    int  *ip;
    int   offset;
    void **routine;

    ip = program;
    p = s;

noop:
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

load:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    r = v[offset];
    goto *routine;

store:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    v[offset] = r;
    goto *routine;

push:
    routine = codes[*(unsigned int *)ip++];
    *p++ = r;
    goto *routine;

add:
    routine = codes[*(unsigned int *)ip++];
    r = *--p + r;
    goto *routine;

inc:
    routine = codes[*(unsigned int *)ip++];
    r = r + 1;
    goto *routine;

lt:
    routine = codes[*(unsigned int *)ip++];
    r = *--p < r;
    goto *routine;

j:
    ip += *(char *)ip;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

jt:
    if (r)
	ip += *(int *)ip;
    else
	++ip;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

imm:
    r = *(long long *)ip;
    ip += 2;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

load_push:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    r = v[offset];
    *p++ = r;
    goto *routine;

store_push:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    v[offset] = r;
    *p++ = r;
    goto *routine;

imm_lt:
    r = *(long long *)ip;
    ip += 2;
    routine = codes[*(unsigned int *)ip++];
    r = *--p < r;
    goto *routine;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 4
typedef struct thread {
    long long	r;
    long long	s[16], *b, *p;
} thread_t;

void v_push(thread_t *t) {
    ++t->p;
    t->p[-1] = t->r;
}
void v_load(thread_t *t, long long i) {
    t->r = t->b[i];
}
void v_store(thread_t *t, long long i) {
    t->b[i] = t->r;
}
void v_add(thread_t *t) {
    t->r = t->p[-1] + t->r;
    --t->p;
}
void v_inc(thread_t *t) {
    ++t->r;
}
void v_imm(thread_t *t, long long i) {
    t->r = i;
}
void v_lt(thread_t *t) {
    t->r = t->p[-1] < t->r;
    --t->p;
}
void
vm(void)
{
    thread_t	t;
#define NOOP	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define J	7
#define JT	8
#define IMM	9
#define END	10
    char program[] = {
	J,	2,	NOOP,	IMM,
	0,	0,	0,	0,
	0,	0,	0,	0,
	STORE,	0,	STORE,	1,
	/* L0: */
	LOAD,	1,	PUSH,	LOAD,
	0,	ADD,	STORE,	1,
	LOAD,	0,	INC,	STORE,
	0,	PUSH,	NOOP,	IMM,
	0x80,	0x96,	0x98,	0,
	0,	0,	0,	0,
	LT,	JT,	-26,	END
    };
    const void *codes[] = {
	&&noop,	&&load,	&&store, &&push,
	&&add,	&&inc,	&&lt,	&&j,
	&&jt,	&&imm,	&&end
    };
    char *ip;

    ip = program;
    t.b = t.s;
    t.p = t.s + 2;
    t.r = 0;

noop:
    goto *codes[*(unsigned char *)ip++];

load:
    v_load(&t, *ip++);
    goto *codes[*(unsigned char *)ip++];

store:
    v_store(&t, *ip++);
    goto *codes[*(unsigned char *)ip++];

push:
    v_push(&t);
    goto *codes[*(unsigned char *)ip++];

add:
    v_add(&t);
    goto *codes[*(unsigned char *)ip++];

inc:
    v_inc(&t);
    goto *codes[*(unsigned char *)ip++];

lt:
    v_lt(&t);
    goto *codes[*(unsigned char *)ip++];

j:
    ip += *(char *)ip;
    goto *codes[*(unsigned char *)ip++];

jt:
    if (t.r)
	ip += *(char *)ip;
    else
	++ip;
    goto *codes[*(unsigned char *)ip++];

imm:
    v_imm(&t, *(long long *)ip);
    ip += 8;
    goto *codes[*(unsigned char *)ip++];

end:
    printf("%lld\n", t.b[1]);
}

#elif VM == 5
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
    void *program[] = {
	&&imm,		(void*)0,	(void*)0,	&&store,
	(void*)0,	&&store,	(void*)1,	/*L:*/&&load,
	(void*)1,	&&push,		&&load,		(void*)0,
	&&add,		&&store,	(void*)1,	&&load,
	(void*)0,	&&inc,		&&store,	(void*)0,
	&&push,		&&imm,		(void*)0x989680,(void*)0,
	&&lt,		&&jt,		(void*)-19,	&&end
    };
    void **ip;

    ip = program;
    p = s;

noop:
    goto **ip++;

load:
    r = v[*(int *)ip++];
    goto **ip++;

store:
    v[*(int *)ip++] = r;
    goto **ip++;

push:
    *p++ = r;
    goto **ip++;

add:
    r = *--p + r;
    goto **ip++;

inc:
    r = r + 1;
    goto **ip++;

lt:
    r = *--p < r;
    goto **ip++;

j:
    ip += *(int *)ip;
    goto **ip++;

jt:
    if (r)
	ip += *(int *)ip;
    else
	++ip;
    goto **ip++;

imm:
    r = *(long long *)ip;
    ip += 2;
    goto **ip++;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 6
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
    void *program[] = {
	&&imm,		(void*)0,	(void*)0,	&&store,
	(void*)0,	&&store,	(void*)1,	/*L:*/&&load_push,
	(void*)1,	&&load,		(void*)0,	&&add,
	&&store,	(void*)1,	&&load,		(void*)0,
	&&inc,		&&store_push,	(void*)0,	&&imm_lt,
	(void*)0x989680,(void*)0,	&&jt,		(void*)-16,
	&&end
    };
    void **ip;

    ip = program;
    p = s;

noop:
    goto **ip++;

load:
    r = v[*(int *)ip++];
    goto **ip++;

load_push:
    r = v[*(int *)ip++];
    *p++ = r;
    goto **ip++;

store:
    v[*(int *)ip++] = r;
    goto **ip++;

store_push:
    v[*(int *)ip++] = r;
    *p++ = r;
    goto **ip++;

push:
    *p++ = r;
    goto **ip++;

add:
    r = *--p + r;
    goto **ip++;

inc:
    r = r + 1;
    goto **ip++;

lt:
    r = *--p < r;
    goto **ip++;

j:
    ip += *(int *)ip;
    goto **ip++;

jt:
    if (r)
	ip += *(int *)ip;
    else
	++ip;
    goto **ip++;

imm:
    r = *(long long *)ip;
    ip += 2;
    goto **ip++;

imm_lt:
    r = *(long long *)ip;
    ip += 2;
    r = *--p < r;
    goto **ip++;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 7
typedef struct thread {
    long long	r;
    long long	s[16], *b, *p;
} thread_t;

void push(thread_t *t) {
    ++t->p;
    t->p[-1] = t->r;
}
void load(thread_t *t, long long i) {
    t->r = t->b[i];
}
void store(thread_t *t, long long i) {
    t->b[i] = t->r;
}
void add(thread_t *t) {
    t->r = t->p[-1] + t->r;
    --t->p;
}
void inc(thread_t *t) {
    ++t->r;
}
void imm(thread_t *t, long long i) {
    t->r = i;
}
void lt(thread_t *t) {
    t->r = t->p[-1] < t->r;
    --t->p;
}
void vm(void) {
    thread_t	t;
    t.b = t.s;
    t.p = t.s + 2;
    t.r = 0;

    imm(&t, 0LL);
    store(&t, 0);
    store(&t, 1);
L1:
    load(&t, 0);
    push(&t);
    load(&t, 1);
    add(&t);
    store(&t, 1);
    load(&t, 0);
    inc(&t);
    store(&t, 0);
    push(&t);
    imm(&t, 10000000LL);
    lt(&t);
    if (t.r)
	goto L1;
    printf("%lld\n", t.b[1]);
}

#elif VM == 8
#include <stddef.h>
#include <lightning.h>

#define NOOP	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define J	7
#define JT	8
#define IMM	9
#define END	10
int program[] = {
    IMM,	0,		STORE,		0,
    STORE,	1,/*L:*/	LOAD,		1,
    PUSH,	LOAD,		0,		ADD,
    STORE,	1,		LOAD,		0,
    INC,	STORE,		0,		PUSH,
    IMM,	0x989680,	LT,		JT,
    -18,	LOAD,		1,		END
};

typedef struct thread {
    int	*ss, *bp, *sp;
} thread_t;

typedef int (*pift_t)(thread_t *);

pift_t compile(int *program, int length) {
    int		 *base;
    jit_insn	 *code;
    jit_insn	**labels;
    int		  thread;

    /* random guess about size */
#if 0
    code = malloc(length * 32 * sizeof(int));
#else
    posix_memalign((void **)&code, 4096,
		   ((length * 32 * sizeof(int)) + 4096) & ~4095);
#endif

    /* XXX no label information here... */
    labels = malloc(length * sizeof(jit_insn*));

    jit_set_ip(code);

    jit_prolog(1);
    thread = jit_arg_p();
    jit_getarg_p(JIT_V1, thread);	/* v1 = thread */

    for (base = program; length > 0; --length, ++program) {
	labels[program - base] = jit_get_label();
	switch (*program) {
	    case NOOP:
		break;
	    case LOAD:
		++program;
		/* r1 = bp */
		jit_ldxi_p(JIT_R1, JIT_V1, offsetof(thread_t, bp));
		/* r0 = r1[offset] */
		jit_ldxi_i(JIT_R0, JIT_R1, *program * sizeof(int));
		break;
	    case STORE:
		++program;
		/* r1 = bp */
		jit_ldxi_p(JIT_R1, JIT_V1, offsetof(thread_t, bp));
		/* r1[offset] = r0 */
		jit_stxi_i(*program * sizeof(int), JIT_R1, JIT_R0);
		break;
	    case PUSH:
		/* r1 = sp */
		jit_ldxi_p(JIT_R1, JIT_V1, offsetof(thread_t, sp));
		/* r2 = r1 + sizeof(int) */
		jit_addi_i(JIT_R2, JIT_R1, sizeof(int));
		/* sp = r2 */
		jit_stxi_p(offsetof(thread_t, sp), JIT_V1, JIT_R2);
		/* *r1 = r0 */
		jit_stxi_i(0, JIT_R1, JIT_R0);
		break;
	    case ADD:
		/* r1 = sp */
		jit_ldxi_p(JIT_R1, JIT_V1, offsetof(thread_t, sp));
		/* r2 = r1 - sizeof(int) */
		jit_subi_i(JIT_R2, JIT_R1, sizeof(int));
		/* r1 = *r2 */
		jit_ldr_i(JIT_R1, JIT_R2);
		/* r0 = r1 + r0 */
		jit_addr_i(JIT_R0, JIT_R1, JIT_R0);
		/* sp = r2 */
		jit_stxi_i(offsetof(thread_t, sp), JIT_V1, JIT_R2);
		break;
	    case INC:
		/* r0 = r0 + 1 */
		jit_addi_i(JIT_R0, JIT_R0, 1);
		break;
	    case LT:
		/* r1 = sp */
		jit_ldxi_p(JIT_R1, JIT_V1, offsetof(thread_t, sp));
		/* r2 = r1 - sizeof(int) */
		jit_subi_i(JIT_R2, JIT_R1, sizeof(int));
		/* r1 = *r2 */
		jit_ldr_i(JIT_R1, JIT_R2);
		/* r0 = r1 < r0 */
		jit_ltr_i(JIT_R0, JIT_R1, JIT_R0);
		/* sp = r2 */
		jit_stxi_i(offsetof(thread_t, sp), JIT_V1, JIT_R2);
		break;
	    case J:
		/* XXX no forward jumps in sample test */
		++program;
		jit_jmpi(labels[program - base + *program]);
		break;
	    case JT:
		/* XXX no forward jumps in sample test */
		++program;
		jit_bnei_ui(labels[program - base + *program], JIT_R0, 0);
		break;
	    case IMM:
		/* r0 = imm */
		++program;
		jit_movi_i(JIT_R0, *program);
		break;
	    case END:
		jit_retval(JIT_R0);
		jit_ret();
		jit_flush_code(code, jit_get_ip().ptr);
		return (pift_t)(code);
	}
    }
    abort();
}

void vm(void) {
    pift_t 	jit;
    thread_t	thread;

    thread.bp = thread.ss = malloc(16 * sizeof(int));
    thread.sp = thread.bp + 2;
    jit = compile(program, sizeof(program) / sizeof(int));
    printf("%d\n", (*jit)(&thread));
}

#elif VM == 9
/* test with "super instructions" */
void
vm(void)
{
    long long r;
    long long v[2];
    long long s[16];
    long long *p;
#define NOOP				0
#define IMM				1
#define STORE				2
#define LOAD_PUSH_LOAD_ADD_STORE	3
#define LOAD_INC_STORE			4
#define PUSH_IMM_LT_JT			5
#define END				6
    int program[] = {
	IMM,	0,	0,		STORE,
	0,	STORE,	1,/*L*/		LOAD_PUSH_LOAD_ADD_STORE,
	1,	0,	1,		LOAD_INC_STORE,
	0,	0,	PUSH_IMM_LT_JT,	0x989680,
	0,	-10,	END
    };
    void *codes[] = {
	&&noop,			&&imm,
	&&store, 		&&load_push_load_add_store,
	&&load_inc_store,	&&push_imm_lt_jt,
	&&end
    };
    int  *ip;
    int   offset;
    void **routine;

    ip = program;
    p = s;

noop:
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

imm:
    r = *(long long *)ip;
    ip += 2;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

store:
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    v[offset] = r;
    goto *routine;

    /* not really required to add to the stack,
      could be load to a second register */
load_push_load_add_store:
    *p++ = v[*ip++];
    r = v[*ip++];
    offset = *ip++;
    routine = codes[*(unsigned int *)ip++];
    r = v[offset] = *--p + r;
    goto *routine;

    /* two offsets for generic usage, but most times it is a single
     * variable that is pushed, also need post increment version,
     * that would be somewhat like push_load_inc_store_pop */
load_inc_store:
    r = v[*ip++] + 1;
    v[*ip++] = r;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

    /* also not required to mimic non "super instruction" format,
     * by pushing r0 to the vm stack */
push_imm_lt_jt:
    *p++ = r;
    r = *(long long *)ip;
    ip += 2;
    r = *--p < r;
    if (r)
	ip += *(int *)ip;
    else
	++ip;
    routine = codes[*(unsigned int *)ip++];
    goto *routine;

end:
    printf("%lld\n", v[1]);
}

#elif VM == 10
#include <stddef.h>
#include <lightning.h>

#define LABEL	0
#define LOAD	1
#define STORE	2
#define PUSH	3
#define ADD	4
#define INC	5
#define LT	6
#define JT	7
#define IMM	8
#define END	9
typedef struct thread {
    long long	 r;
    long long	*ss, *bp, *sp;
} thread_t;

typedef void (*pvft_t)(void);

static thread_t	thread;

int program[] = {
    IMM,	0,		0,		STORE,
    0,		STORE,		1,		LABEL,
    LOAD,	1,		PUSH,		LOAD,
    0,		ADD,		STORE,		1,
    LOAD,	0,		INC,		STORE,
    0,		PUSH,		IMM,		0x989680,
    0,		LT,		JT,		-19,
    LOAD,	1,		END
};

static void code_imm(long long *imm) {
    thread.r = *imm;
}

static void code_load(int off) {
    thread.r = thread.bp[off];
}

static void code_store(int off) {
    thread.bp[off] = thread.r;
}

static void code_push(void) {
    *thread.sp++ = thread.r;
}

static void code_add(void) {
    thread.r = *--thread.sp + thread.r;
}

static void code_inc(void) {
    ++thread.r;
}

static int code_lt(void) {
    return (*--thread.sp < thread.r);
}

pvft_t compile(int *program, int length) {
    int		  pend;
    int		 *base;
    jit_insn	 *code;
    jit_insn	**labels;
    int		  thread;

    /* random guess about size */
#if 0
    code = malloc(length * 32 * sizeof(int));
#else
    posix_memalign((void **)&code, 4096,
		   ((length * 32 * sizeof(int)) + 4096) & ~4095);
#endif

    /* XXX no label information here... */
    labels = malloc(length * sizeof(jit_insn*));

    jit_set_ip(code);
    jit_prolog(0);
    for (pend = 0, base = program; length > 0; --length, ++program) {
	if (pend) {
	    labels[program - base] = jit_get_label();
	    pend = 0;
	}
	switch (*program) {
	    case LABEL:
		pend = 1;
		break;
	    case LOAD:
		++program;
		jit_prepare(1);
		jit_movi_i(JIT_R0, *program);
		jit_pusharg_i(JIT_R0);
		jit_finish(code_load);
		break;
	    case STORE:
		++program;
		jit_prepare(1);
		jit_movi_i(JIT_R0, *program);
		jit_pusharg_i(JIT_R0);
		jit_finish(code_store);
		break;
	    case PUSH:
		jit_prepare(0);
		jit_finish(code_push);
		break;
	    case ADD:
		jit_prepare(0);
		jit_finish(code_add);
		break;
	    case INC:
		jit_prepare(0);
		jit_finish(code_inc);
		break;
	    case LT:
		jit_prepare(0);
		jit_finish(code_lt);
		break;
	    case JT:
		/* XXX no forward jumps in sample test */
		++program;
		jit_bnei_ui(labels[program - base + *program], JIT_RET, 0);
		break;
	    case IMM:
		++program;
		jit_prepare(1);
		jit_movi_p(JIT_R0, program);
		jit_pusharg_p(JIT_R0);
		jit_finish(code_imm);
		++program;
		break;
	    case END:
		jit_ret();
		jit_flush_code(code, jit_get_ip().ptr);
		return ((pvft_t)code);
	}
    }
    abort();
}

void vm(void) {
    pvft_t 	jit;

    thread.bp = thread.ss = malloc(16 * sizeof(long long));
    thread.sp = thread.bp + 2;
    jit = compile(program, sizeof(program) / sizeof(int));
    (*jit)();
    printf("%lld\n", thread.bp[1]);
}
#endif

int
main(int argc, char *argv[])
{
    vm();
    return 0;
}
